﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using YArchitech.Revit;
using YJKArch.Utils;
using HYOpeningCommon.DataEnum;
using HYOpeningCommon.Public;

namespace TrackDim
{
	public class TrackDimensions
	{
		public TrackDimensions(Autodesk.Revit.DB.Document doc, XYZ ptOri, XYZ ptStart, XYZ ptEnd, string strHostGUID, bool isHavingVector)
		{
			this.m_doc = doc;
			this.m_ptOri = ptOri;
			this.m_ptStart = ptStart;
			this.m_ptEnd = ptEnd;
			this.m_vector = (ptEnd - ptStart).Normalize();
			this.m_strHostGUID = strHostGUID;
			this.m_isHavingVector = isHavingVector;
			this.m_lstLinkIns = TrackDimensions.GetLinkIns(doc);
		}

		public XYZ GetRecentPt(XYZ ptMin, XYZ ptMax, double dOffset, List<BuiltInCategory> lstCategory)
		{
			XYZ intersectGridPt = this.GetIntersectGridPt();
			XYZ wallColumnRecentPt = this.GetWallColumnRecentPt(ptMin, ptMax, dOffset, lstCategory);
			return this.GetRecentPtAt2pts(intersectGridPt, wallColumnRecentPt);
		}

		private XYZ GetRecentPtAt2pts(XYZ pt1, XYZ pt2)
		{
			if (pt1 == null)
			{
				return pt2;
			}
			if (pt2 == null)
			{
				return pt1;
			}
			double val = this.m_ptOri.DistanceTo(pt1);
			double val2 = this.m_ptOri.DistanceTo(pt2);
			if (Geometry.Lessthan_Or_Equal(val, val2))
			{
				return pt1;
			}
			return pt2;
		}

		private XYZ GetIntersectGridPt()
		{
			Transform transform = null;
			Grid grid = this.GetGrid(ref transform);
			if (grid == null)
			{
				return null;
			}
			Line line = grid.Curve as Line;
			if (transform != null)
			{
				line = (line.CreateTransformed(transform) as Line);
			}
			return line.Project(this.m_ptOri).XYZPoint;
		}

		private XYZ GetWallColumnRecentPt(XYZ ptMin, XYZ ptMax, double dOffset, List<BuiltInCategory> lstCategory)
		{
			new List<Element>();
			Dictionary<Element, Transform> dicElem = this.ElementsAtBoundingBox(ptMin, ptMax, lstCategory, false);
			Dictionary<Element, Transform> dicElem2 = this.ElementsAtBoundingBox(ptMin, ptMax, lstCategory, true);
			XYZ recentPt = this.GetRecentPt(dicElem, dOffset);
			XYZ recentPt2 = this.GetRecentPt(dicElem2, dOffset);
			return this.GetRecentPtAt2pts(recentPt, recentPt2);
		}

		private XYZ GetRecentPt(Dictionary<Element, Transform> dicElem, double dOffset)
		{
			List<Face> lstFace = new List<Face>();
			XYZ result = null;
			double val = double.MaxValue;
			foreach (KeyValuePair<Element, Transform> keyValuePair in dicElem)
			{
				Element key = keyValuePair.Key;
				Transform value = keyValuePair.Value;
				if (key != null)
				{
					XYZ vector = this.m_vector;
					if (value != null)
					{
						vector = value.Inverse.OfVector(this.m_vector);
					}
					if (key is FamilyInstance && key.Category.Id.IntegerValue == -2000100)
					{
						lstFace = this.GetAllPlanarFace(key, vector);
						XYZ interSecPt = this.GetInterSecPt(lstFace, value);
						if (interSecPt != null)
						{
							double num = this.m_ptOri.DistanceTo(interSecPt);
							if (Geometry.LessThan(num, val))
							{
								result = interSecPt;
								val = num;
							}
						}
					}
					else
					{
						lstFace = this.GetAllPlanarFace(key, vector);
						if (this.LineCrossEelm(this.m_ptEnd, dOffset, lstFace, value))
						{
							XYZ interSecPt2 = this.GetInterSecPt(this.m_ptEnd, lstFace, value);
							if (interSecPt2 != null)
							{
								double num2 = this.m_ptOri.DistanceTo(interSecPt2);
								if (Geometry.LessThan(num2, val))
								{
									result = interSecPt2;
									val = num2;
								}
							}
						}
					}
				}
			}
			return result;
		}

		private XYZ GetInterSecPt(List<Face> lstFace, Transform transform)
		{
			if (!this.IsIntersectColumn(lstFace))
			{
				return null;
			}
			XYZ xyz;
			XYZ xyz2;
			XYZ xyz3;
			if (transform != null)
			{
				xyz = transform.Inverse.OfPoint(this.m_ptStart);
				xyz2 = transform.Inverse.OfPoint(this.m_ptEnd);
				xyz3 = transform.Inverse.OfPoint(this.m_ptOri);
			}
			else
			{
				xyz = this.m_ptStart;
				xyz2 = this.m_ptEnd;
				xyz3 = this.m_ptOri;
			}
			Line line = Line.CreateBound(xyz, xyz2);
			double val = double.MaxValue;
			XYZ xyz4 = null;
			foreach (Face face in lstFace)
			{
				PlanarFace planarFace = face as PlanarFace;
				XYZ xyzpoint = line.Project(planarFace.Origin).XYZPoint;
				if (xyzpoint != null)
				{
					double num = xyzpoint.DistanceTo(xyz3);
					if (Geometry.LessThan(num, val))
					{
						val = num;
						xyz4 = xyzpoint;
					}
				}
			}
			if (xyz4 == null)
			{
				return null;
			}
			if (transform != null)
			{
				return transform.OfPoint(xyz4);
			}
			return xyz4;
		}

		private bool IsIntersectColumn(List<Face> lstFace)
		{
			Element element = this.m_doc.GetElement(this.m_strHostGUID);
			if (!(element is Wall))
			{
				return true;
			}
			Wall wall = element as Wall;
			Line line = (wall.Location as LocationCurve).Curve as Line;
			if (line == null)
			{
				return false;
			}
			XYZ direction = line.Direction;
			List<Face> allPlanarFace = this.GetAllPlanarFace(wall, direction.CrossProduct(XYZ.BasisZ));
			List<Line> edges = this.GetEdges(allPlanarFace);
			IntersectionResultArray intersectionResultArray = new IntersectionResultArray();
			foreach (Face face in lstFace)
			{
				PlanarFace planarFace = face as PlanarFace;
				if (!(planarFace == null))
				{
					foreach (Line line2 in edges)
					{
						planarFace.Intersect(line2, out intersectionResultArray);
						if (intersectionResultArray != null && !intersectionResultArray.IsEmpty)
						{
							return true;
						}
					}
				}
			}
			return false;
		}

		private List<Line> GetEdges(List<Face> lstFace)
		{
			List<Line> list = new List<Line>();
			foreach (Face face in lstFace)
			{
				foreach (object obj in face.EdgeLoops)
				{
					foreach (object obj2 in ((EdgeArray)obj))
					{
						Line line = ((Edge)obj2).AsCurve() as Line;
						if (line != null)
						{
							list.Add(line);
						}
					}
				}
			}
			return list;
		}

		private Grid GetGrid(ref Transform transform)
		{
			List<Grid> lstGrids = new List<Grid>();
			this.GetGrids(ref lstGrids, ref transform);
			return this.GetRecentVecticalGrid(lstGrids, transform);
		}

		private Grid GetRecentVecticalGrid(List<Grid> lstGrids, Transform transform)
		{
			double num = double.MaxValue;
			Grid result = null;
			foreach (Grid grid in lstGrids)
			{
				if (grid != null)
				{
					Line line = grid.Curve as Line;
					if (!(line == null))
					{
						if (transform != null)
						{
							line = (line.CreateTransformed(transform) as Line);
						}
						XYZ xyz = this.m_vector.Normalize();
						if (!this.m_isHavingVector || Geometry.IsEqual(Math.Abs(line.Direction.AngleTo(xyz)), Math.PI*.5) || Geometry.IsEqual(Math.Abs(line.Direction.AngleTo(xyz.Negate())), Math.PI*.5))
						{
							double num2 = line.Project(this.m_ptOri).XYZPoint.DistanceTo(this.m_ptOri);
							if (num2 < num)
							{
								num = num2;
								result = grid;
							}
						}
					}
				}
			}
			return result;
		}

		private void GetGrids(ref List<Grid> lstGrids, ref Transform transfrom)
		{
			if (!this.GetGridsByDoc(ref lstGrids, this.m_doc))
			{
				foreach (RevitLinkInstance revitLinkInstance in this.m_lstLinkIns)
				{
					Document linkDocument = revitLinkInstance.GetLinkDocument();
					if (this.GetGridsByDoc(ref lstGrids, linkDocument))
					{
						transfrom = revitLinkInstance.GetTotalTransform();
						break;
					}
				}
			}
		}

		private bool GetGridsByDoc(ref List<Grid> lstGrids, Autodesk.Revit.DB.Document doc)
		{
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(doc);
			lstGrids = filteredElementCollector.OfCategory(BuiltInCategory.OST_Grids).WhereElementIsNotElementType().Cast<Grid>().ToList<Grid>();
			return lstGrids.Count != 0;
		}

		private Dictionary<Element, Transform> ElementsAtBoundingBox(XYZ ptMin, XYZ ptMax, List<BuiltInCategory> lstCategory, bool isLinkDoc)
		{
			double elevation = this.m_doc.ActiveView.GenLevel.Elevation;
			BoundingBoxIntersectsFilter filter = new BoundingBoxIntersectsFilter(new Outline(ptMin, ptMax));
			new List<Element>();
			Dictionary<Element, Transform> dictionary = new Dictionary<Element, Transform>();
			if (!isLinkDoc)
			{
				using (List<Element>.Enumerator enumerator = this.GetElemAtBoundingBox(this.m_doc, lstCategory, filter).GetEnumerator())
				{
					while (enumerator.MoveNext())
					{
						Element key = enumerator.Current;
						dictionary.Add(key, null);
					}
					return dictionary;
				}
			}
			foreach (RevitLinkInstance revitLinkInstance in this.m_lstLinkIns)
			{
				Document linkDocument = revitLinkInstance.GetLinkDocument();
				if (linkDocument != null)
				{
					Transform totalTransform = revitLinkInstance.GetTotalTransform();
					foreach (Element key2 in this.GetElemAtBoundingBox(linkDocument, lstCategory, filter))
					{
						dictionary.Add(key2, totalTransform);
					}
				}
			}
			return dictionary;
		}

		private List<Element> GetElemAtBoundingBox(Autodesk.Revit.DB.Document doc, List<BuiltInCategory> lstCategory, BoundingBoxIntersectsFilter filter)
		{
			List<Element> list = new List<Element>();
			new List<Element>();
			foreach (BuiltInCategory builtInCategory in lstCategory)
			{
				foreach (Element element in new FilteredElementCollector(doc).OfCategory(builtInCategory).WherePasses(filter).ToList<Element>())
				{
					if (!(element.UniqueId == this.m_strHostGUID) && (!(element is Wall) || CommonInterface.GetSubjectObj(element) != EHostObject.eArchWall))
					{
						list.Add(element);
					}
				}
			}
			return list;
		}

		private bool LineCrossEelm(XYZ ptEnd, double width, List<Face> lstFace, Transform transform)
		{
			if (!this.m_isHavingVector)
			{
				return true;
			}
			XYZ xyz = this.m_ptStart + this.m_vector.Negate().Normalize() * width;
			XYZ xyz2 = ptEnd + this.m_vector.Normalize() * width;
			Line line = Line.CreateBound(xyz, xyz2);
			if (transform != null)
			{
				line = (line.CreateTransformed(transform.Inverse) as Line);
			}
			IntersectionResultArray intersectionResultArray = null;
			foreach (Face face in lstFace)
			{
				face.Intersect(line, out intersectionResultArray);
				if (intersectionResultArray != null && !intersectionResultArray.IsEmpty && intersectionResultArray.get_Item(0).XYZPoint != null)
				{
					return true;
				}
			}
			return false;
		}

		private XYZ GetInterSecPt(XYZ ptEnd, List<Face> lstFace, Transform transform)
		{
			XYZ xyz = null;
			double num = double.MaxValue;
			XYZ ptStart;
			XYZ ptEnd2;
			XYZ xyz2;
			if (transform != null)
			{
				ptStart = transform.Inverse.OfPoint(this.m_ptStart);
				ptEnd2 = transform.Inverse.OfPoint(ptEnd);
				xyz2 = transform.Inverse.OfPoint(this.m_ptOri);
			}
			else
			{
				ptStart = this.m_ptStart;
				ptEnd2 = ptEnd;
				xyz2 = this.m_ptOri;
			}
			foreach (Face face in lstFace)
			{
				PlanarFace planarFace = face as PlanarFace;
				if (!(planarFace == null))
				{
					XYZ xyz3;
					if (this.m_isHavingVector)
					{
						xyz3 = Geometry.CalculateFootPoint(ptStart, ptEnd2, planarFace.Origin);
					}
					else
					{
						IntersectionResult intersectionResult = planarFace.Project(xyz2);
						if (intersectionResult == null)
						{
							continue;
						}
						xyz3 = intersectionResult.XYZPoint;
					}
					if (xyz3 != null)
					{
						double num2 = xyz2.DistanceTo(xyz3);
						if (num2 > 1E-09 && num2 < num)
						{
							xyz = xyz3;
							num = num2;
						}
					}
				}
			}
			if (xyz == null)
			{
				return null;
			}
			if (transform != null)
			{
				return transform.OfPoint(xyz);
			}
			return xyz;
		}

		private List<Face> GetAllPlanarFace(Element elem, XYZ vector)
		{
			List<Face> result;
			try
			{
				IEnumerator<GeometryObject> enumerator = elem.get_Geometry(new Options
				{
					ComputeReferences = false,
					IncludeNonVisibleObjects = true
				}).GetEnumerator();
				List<Face> list = new List<Face>();
				while (enumerator.MoveNext())
				{
					GeometryObject geometryObject = enumerator.Current;
					if (geometryObject is PlanarFace)
					{
						XYZ xyz = (geometryObject as PlanarFace).FaceNormal();
						if (!this.m_isHavingVector || xyz.Normalize().IsAlmostEqualTo(vector) || xyz.Normalize().IsAlmostEqualTo(vector.Negate()))
						{
							list.Add(geometryObject as PlanarFace);
						}
					}
					else if (geometryObject is Solid)
					{
						Solid solid = geometryObject as Solid;
						if (!this.GetFaceBySolid(solid, ref list, vector))
						{
							return null;
						}
					}
					else if (geometryObject is GeometryInstance)
					{
						foreach (GeometryObject geometryObject2 in (geometryObject as GeometryInstance).SymbolGeometry)
						{
							Solid solid2 = geometryObject2 as Solid;
							if (!this.GetFaceBySolid(solid2, ref list, vector))
							{
								return null;
							}
						}
					}
				}
				result = list;
			}
			catch (Exception)
			{
				result = null;
			}
			return result;
		}

		private bool GetFaceBySolid(Solid solid, ref List<Face> lstFace, XYZ normal)
		{
			bool result;
			try
			{
				if (solid != null)
				{
					foreach (object obj in solid.Faces)
					{
						Face face = (Face)obj;
						if (face.GetType() == typeof(PlanarFace))
						{
							XYZ xyz = (face as PlanarFace).FaceNormal();
							if (!this.m_isHavingVector || xyz.Normalize().IsAlmostEqualTo(normal) || xyz.Normalize().IsAlmostEqualTo(normal.Negate()))
							{
								lstFace.Add(face);
							}
						}
					}
				}
				result = true;
			}
			catch (Exception)
			{
				result = false;
			}
			return result;
		}

		private static List<RevitLinkInstance> GetLinkIns(Autodesk.Revit.DB.Document doc)
		{
			List<RevitLinkInstance> result;
			try
			{
				new List<Document>();
				result = new FilteredElementCollector(doc).OfClass(typeof(RevitLinkInstance)).WhereElementIsNotElementType().Cast<RevitLinkInstance>().ToList<RevitLinkInstance>();
			}
			catch (Exception)
			{
				result = null;
			}
			return result;
		}

		private Document m_doc;

		private XYZ m_ptOri;

		private XYZ m_ptStart;

		private XYZ m_ptEnd;

		private XYZ m_vector;

		private List<RevitLinkInstance> m_lstLinkIns;

		private string m_strHostGUID;

		private bool m_isHavingVector;
	}
}
